package com.afdor.rws.event.channel.local.observer;

import com.afdor.rws.event.Event;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;

/**
 * 事件监听器管理容器，该类中，事件堆积及监听器等操作<font color="red">均为多线程安全</font>的，故可放心使用
 *
 * @author 悭梵
 * @date Created in 2018-08-08 15:12
 */
@Slf4j
@ToString
@EqualsAndHashCode
public class ObserverEventSource {
    /**
     * 事件队列
     */
    @Getter
    private final Queue<Event> eventQueue;
    /**
     * 事件监听器列表
     */
    @Getter
    private final ConcurrentMap<String, List<ObserverEventListener>> listenerMap;

    public ObserverEventSource() {
        this.eventQueue = new ConcurrentLinkedQueue();
        this.listenerMap = new ConcurrentHashMap<String, List<ObserverEventListener>>();
    }

    /**
     * 给事件源注册监听器
     *
     * @param listener
     */
    public void addEventListener(@NonNull String eventSourceName, @NonNull ObserverEventListener listener) {
        List<ObserverEventListener> listeners = this.listenerMap.get(eventSourceName);
        if (listeners == null) {
            this.listenerMap.putIfAbsent(eventSourceName, new ArrayList<ObserverEventListener>());
            listeners = this.listenerMap.get(eventSourceName);
        }
        listeners.add(listener);
    }

    /**
     * 清空监听器
     */
    public void clearEventListener() {
        this.listenerMap.clear();
    }

    /**
     * 当事件发生时,将事件加入队列
     *
     * @param event
     */
    public void send(Event event) {
        eventQueue.add(event);
    }

    public boolean hasEvent() {
        return this.eventQueue.peek() != null;
    }

    /**
     * 从队列头开始，消费事件
     */
    public boolean consume() {
        // TODO 执行重试策略
        Event event = this.eventQueue.poll();
        if (event != null && StringUtils.isNotBlank(event.getEventSourceName())) {
            boolean successed = Boolean.TRUE;
            ObserverEventListener listener = null;
            Iterator<ObserverEventListener> iterator = this.listenerMap.get(event.getEventSourceName()).iterator();
            while (iterator.hasNext()) {
                try {
                    listener = iterator.next();
                    listener.onEvent(event);
                } catch (Exception e) {
                    log.error("", e);
                    successed = Boolean.FALSE;
                }
            }
            return successed;
        }
        return Boolean.FALSE;
    }

}